package com.hp.springboot.exception;

import com.hp.springboot.result.CodeMsg;
import com.hp.springboot.result.Result;
import java.util.List;
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageConversionException;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;

/**
 * 全局异常处理
 *
 * @author hupan
 * @date 2021-12-16
 */
@Slf4j
@ResponseBody
@ControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(value = Exception.class)
    public Result<Object> exceptionHandler(HttpServletRequest request, Exception e) {
        if (e instanceof BizException) {
            BizException bizException = (BizException) e;
            return Result.error(bizException.getCode(), bizException.getMessage());
        }

        log.error("===> 系统异常", e);
        return Result.error(CodeMsg.SERVER_ERROR.detail(""));
    }

    @ResponseBody
    @ExceptionHandler(BizException.class)
    public Result<Object> handleControllerException(HttpServletRequest request, Throwable e) {
        if (e instanceof BizException) {
            BizException ex = (BizException) e;
            return Result.error(ex.getCodeMsg());
        } else if (e instanceof BindException) {
            BindException ex = (BindException) e;
            List<ObjectError> errors = ex.getAllErrors();
            ObjectError error = errors.get(0);
            String msg = error.getDefaultMessage();

            log.error(CodeMsg.BIND_ERROR.getMsg() + "：" + msg);
            return Result.error(CodeMsg.BIND_ERROR).detail(msg);
        } else {
            log.error("===> 系统异常", e);
            return Result.error(CodeMsg.SERVER_ERROR);
        }

    }

    private HttpStatus getStatus(HttpServletRequest request) {
        Integer statusCode = (Integer) request.getAttribute("javax.servlet.error.status_code");
        if (statusCode == null) {
            return HttpStatus.INTERNAL_SERVER_ERROR;
        }

        return HttpStatus.valueOf(statusCode);
    }

    @ExceptionHandler(value = {MethodArgumentNotValidException.class})
    public Result<Object> handleMethodArgumentNotValidException(HttpServletRequest request, MethodArgumentNotValidException e) {
        BindingResult result = e.getBindingResult();
        FieldError error = result.getFieldError();

        if (error != null) {
            String field = error.getField();
            String detail = error.getDefaultMessage();
            String message = String.format("%s-%s", field, detail);

            log.error(CodeMsg.BIND_ERROR.getMsg() + "：" + message);
            Result.error(CodeMsg.BIND_ERROR).detail(message);
        }

        return Result.error(CodeMsg.BIND_ERROR);
    }

    @ExceptionHandler(BindException.class)
    public Result<Object> handleBindException(HttpServletRequest request, BindException e) {
        BindException ex = (BindException) e;
        String field = ex.getFieldErrors().get(0).getField();
        ObjectError error = ex.getAllErrors().get(0);

        if (error.contains(IllegalArgumentException.class)) {
            log.error(CodeMsg.INVALID_PARAMETERS.getMsg() + "：" + field);
            return Result.error(CodeMsg.INVALID_PARAMETERS).detail(field);
        } else {
            log.error(CodeMsg.INVALID_PARAMETERS.getMsg() + "：" + error.getDefaultMessage());
            return Result.error(CodeMsg.BIND_ERROR).detail(error.getDefaultMessage());
        }
    }

    @ExceptionHandler(ConstraintViolationException.class)
    public Result<Object> handleConstraintViolationException(HttpServletRequest request, ConstraintViolationException e) {
        Set<ConstraintViolation<?>> violations = e.getConstraintViolations();
        ConstraintViolation<?> violation = violations.iterator().next();
        String message = violation.getMessage();

        log.error(CodeMsg.BIND_ERROR.getMsg() + "：" + message);
        return Result.error(CodeMsg.BIND_ERROR.getMsg(), message);
    }

    @ExceptionHandler(MissingServletRequestParameterException.class)
    public Result<Object> handleMissingServletRequestParameterException(HttpServletRequest request,
        MissingServletRequestParameterException e) {
        String name = e.getParameterName();

        log.error(CodeMsg.BIND_ERROR.getMsg() + "，缺少参数：" + name);
        return Result.error(CodeMsg.BIND_ERROR);
    }

    @ExceptionHandler(MethodArgumentTypeMismatchException.class)
    public Result<Object> handleMethodArgumentTypeMismatchException(HttpServletRequest request, MethodArgumentTypeMismatchException ex) {
        String name = ex.getName();
        String type = ex.getRequiredType().getSimpleName();
        Object value = ex.getValue();
        String message = String.format("参数 '%s' 应该为 '%s' 类型", name, type);

        log.error(CodeMsg.BIND_ERROR.getMsg() + "：" + message);
        return Result.error(CodeMsg.BIND_ERROR);
    }

}
